/*
 * Students:
 * Massimiliano Rango - 610826
 * Martina Benedetto - 610962
 * 
 * A.A 2011/2012
 * 
 */

import java.text.ParseException;

/**
 *
 * This class represents the parameters of a simulation. This class has also 
 * some fields to store whether the node has been found or not, the number of
 * nodes that have done location claim, the number of circulating messages and
 * the number of killed nodes.
 * 
 * @version 1.0 27 Dec 2011
 * @author Massimiliano Rango
 *
 */
public class Configuration {
	
	public enum Protocol { RED, LSM };
	public Object lockEndSimulation; /* represents a lock for the end of the simulation */

	private Protocol m_protocol;
	private int m_nSim;
	private int m_nNodes;
	private float m_radius;
	private float m_prob;
	private int m_g;
	private int m_totEnergy;
	private int m_recEnergy;
	private int m_sendEnergy;
	private int m_sigEnergy;
	private int m_rand;
	
	private boolean m_cloneFound;
	private int m_nodeLocClaim;
	private int m_circulatingMess;
	private int m_killedNodes;
	
	/**
	 * Creates a new Configuration object with the given parameters
	 * @param protocol Protocol used during the simulations
	 * @param nSim Number of simulations to execute
	 * @param nNodes Number of nodes in the network
	 * @param radius Neighbourhood radius of each node
	 * @param prob Probability to forward the message
	 * @param g Number of times to forward the message
	 * @param totEnergy Total energy of each node at the beginning of the 
	 * simulation
	 * @param recEnergy Energy spent each time a node receives a message
	 * @param sendEnergy Energy spent each time a node sends a message
	 * @param sigEnergy Energy spent each time a node verifies the signature
	 * of a message
	 * @throws ParseException If there are some invalid parameters
	 */
	public Configuration(Protocol protocol, int nSim, int nNodes, float radius, 
			float prob, int g, int totEnergy, int recEnergy, 
			int sendEnergy, int sigEnergy) throws ParseException {
		
		if (nSim <= 0) {
			throw new ParseException("nSim must be positive.", 0);
		}
		if (nNodes <= 0) {
			throw new ParseException("nNodes must be positive.", 0);
		}
		if (radius <= 0) {
			throw new ParseException("radius must be positive.", 0);
		}
		if (prob < 0 ||prob > 1) {
			throw new ParseException("prob must be between 0 and 1.", 0);
		}
		if (g <= 0) {
			throw new ParseException("g must be positive.", 0);
		}
		if (totEnergy <= 0) {
			throw new ParseException("totEnergy must be positive.", 0);
		}	
		if (recEnergy < 0) {
			throw new ParseException("recEnergy must be positive or null.", 0);
		}
		if (sendEnergy < 0) {
			throw new ParseException("sendEnergy must be positive or null.", 0);
		}
		if (sigEnergy < 0) {
			throw new ParseException("recEnergy must be positive or null.", 0);
		}
		
		lockEndSimulation = new Object();
		
		m_protocol = protocol;
		m_nSim = nSim;
		m_nNodes = nNodes;
		m_radius = radius;
		m_prob = prob;
		m_g = g;
		m_totEnergy = totEnergy;
		m_recEnergy = recEnergy;
		m_sendEnergy = sendEnergy;
		m_sigEnergy = sigEnergy;
		m_rand = -1;

		m_cloneFound = false;
		m_nodeLocClaim = 0;
		m_circulatingMess = 0;
		m_killedNodes = 0;
	}
	
	/**
	 * Copy constructor
	 * @param config Configuration object we want to copy
	 * @throws ParseException If there are some invalid parameters
	 */
	public Configuration(Configuration config) throws ParseException{
		this(config.m_protocol, config.m_nSim, config.m_nNodes, 
				config.m_radius, config.m_prob, config.m_g, 
				config.m_totEnergy, config.m_recEnergy, 
				config.m_sendEnergy, config.m_sigEnergy);
	}
	
	public Protocol getProtocol() {
		return m_protocol;
	}
	
	public int getNSim() {
		return m_nSim;
	}
	
	public int getNNodes() {
		return m_nNodes;
	}
	
	public float getRadius() {
		return m_radius;
	}
	
	public float getProb() {
		return m_prob;
	}
	
	public int getG() {
		return m_g;
	}
	
	public int getTotEnergy() {
		return m_totEnergy;
	}
	
	public int getRecEnergy() {
		return m_recEnergy;
	}
  
	public int getSendEnergy() {
		return m_sendEnergy;
	}
	
	public int getSigEnergy() {
		return m_sigEnergy;
	}
	
	public int getRand(){
		return m_rand;
	}
	
	public boolean getCloneFound() {
		synchronized(lockEndSimulation) {
			return m_cloneFound;
		}
	}
	
	public int getNodeLocClaim() {
		synchronized(lockEndSimulation) {
			return m_nodeLocClaim;
		}
	}
	
	public int getCirculatingMess() {
		synchronized(lockEndSimulation) {
			return m_circulatingMess;
		}
	}
	
	public int getKilledNodes() {
		synchronized(lockEndSimulation) {
			return m_killedNodes;
		}
	}
	
	public void setRand(int rand) {
		m_rand = rand;
	}
	
	
	/**
	 * Sets m_cloneFound to true and notifies the lockEndSimulation 
	 * because the simulation has ended.
	 */
	public void setCloneFound() {
		synchronized(lockEndSimulation) {
			m_cloneFound = true;
			lockEndSimulation.notifyAll();
		}
	}
	
	/**
	 * Adds a node which has had the chance to do location claim.
	 * If every node has had this chance, this method notifies the 
	 * lockEndSimulation because the simulation can go on.
	 */
	public void addNodeLocClaim() {
		synchronized(lockEndSimulation) {
			m_nodeLocClaim++;
			if (m_nodeLocClaim == m_nNodes + 1) { 
				lockEndSimulation.notifyAll();
			}
		}
	}
	
	public void addCirculatingMess() {
		synchronized(lockEndSimulation) {
			m_circulatingMess++;
		}
	}
	
	/**
	 * Adds a killed node. If all the nodes are dead this method notifies
	 * the lockEndSimulation beacause the simulation has ended.
	 */
	public void addKilledNode() {
		synchronized(lockEndSimulation) {
			m_killedNodes++;
			if (m_killedNodes == m_nNodes + 1) {
				lockEndSimulation.notifyAll();
			}
		}
	}
	
	/**
	 * Removes n messages from circulating. If there aren't circulating 
	 * message this method notifies the lockEndSimulation beacuse 
	 * the simulation has ended.
	 * @param n Number of circulating message to remove.
	 */
	public void removeCirculatingMess(int n) {
		if(n > 0) {
			synchronized(lockEndSimulation) {
				m_circulatingMess -= n;
				if (m_circulatingMess == 0) {
					lockEndSimulation.notifyAll();
				}
			}
		}
	}
	
	public String toString() {
		return "PROTO = " + m_protocol + "\nNSIM = " + m_nSim + "\np = " 
			+ m_prob + "\ng = " + m_g + "\nn = " + m_nNodes	
			+ "\nr = " + m_radius + "\nE = " + m_totEnergy 
			+ "\nE_send = " + m_sendEnergy + "\nE_receive = " 
			+ m_recEnergy + "\nE_signature = " + m_sigEnergy + "\n";
	}
}